<?php

namespace fastflow\lib;

use fastflow\adapter\agencyAdapter;
use fastflow\adapter\carbonAdapter;
use fastflow\adapter\toolAdapter;
use fastflow\adapter\flowAdapter;
use fastflow\adapter\logAdapter;
use fastflow\adapter\processAdapter;
use fastflow\adapter\threadAdapter;
use fastflow\adapter\workerAdapter;
use Tx\Mailer;


class lib
{

    private $msgObject = null;

    public function __construct($msgObject = null)
    {
        $this->msgObject = $msgObject;
    }


    public function getScope()
    {
        return [
            ['value' => 1, 'name' => '指定人员'],
            ['value' => 2, 'name' => '指定分组'],
            ['value' => 3, 'name' => '单据人员(读取单据指定字段值作为办理人ID)'],
            ['value' => 4, 'name' => '单据分组(读取单据指定字段值作为办理分组ID)'],
            ['value' => 5, 'name' => '上级审批(由单据创建人的上级分组成员审批)'],
            ['value' => 6, 'name' => '由上一审批人动态指定']
        ];
    }


    public function getSignScope()
    {
        return [
            ['value' => 1, 'name' => '指定人员'],
            ['value' => 2, 'name' => '指定分组'],
        ];
    }

    public function getCarbonScope()
    {
        return [
            ['value' => 1, 'name' => '人员'],
            ['value' => 2, 'name' => '分组'],
        ];
    }

    public function getDynamicSelectScope()
    {
        return [
            ['value' => 1, 'name' => '指定人员'],
            ['value' => 2, 'name' => '指定分组'],
            ['value' => 5, 'name' => '发起人上级审批'],
        ];
    }


    public function getOperator()
    {
        return [
            ['value' => '=', 'name' => '等于（=）'],
            ['value' => '>', 'name' => '大于（>）'],
            ['value' => '<', 'name' => '小于（<）'],
            ['value' => '>=', 'name' => '大于等于（>=）'],
            ['value' => '<=', 'name' => '小于等于（<=）'],
            ['value' => '<>', 'name' => '不等于（!=）'],
        ];
    }

    public function getCarbonRange()
    {
        return [
            ['value' => 1, 'name' => '当前步骤'],
            ['value' => 2, 'name' => '所有步骤'],
        ];
    }

    public function buidMessageParams($params)
    {
        $message_params = [
            'flowName' => '',
            'flowDescription' => '',
            'scope' => 1,
            'scopeName' => '',
            'workerIds' => '',
            'workerNames' => '',
            'checkmode' => '',
            'stepName' => '',
            'preStepName' => '',
            'bill' => '',
            'billId' => '',
            'billName' => '',
            'agency' => '否',
            'agentId' => '',
            'agentName' => '',
            'principalIds' => '',
            'principalName' => '',
            'receivers' => [],
        ];

        if ($params['type'] == 'step') {
            $thread = self::getThread(['id' => $params['thread_id']])[0];
            $scope_list = self::getScope();
            for ($i = 0; $i < count($scope_list); $i++) {
                if ($scope_list[$i]['value'] == $thread['scope']) {
                    $scope_name = $scope_list[$i]['name'];
                    break;
                }
            }
            $scope_map = [1 => 1, 2 => 2, 3 => 1, 4 => 2, 5 => 2];
            $prestep_id = self::getThread(['id' => $thread['prethread_id']])[0]['step_id'];
            $process = self::getProcess(['id' => $thread['process_id']])[0];
            $agency_info = self::checkAgency($process['bill'], $process['bill_id'], $thread['scope'], self::getWorkerIds($process['bill'], $process['bill_id'], $thread));
            $worker_ids = self::getWorkerIds($process['bill'], $process['bill_id'], $thread);

            $message_params['flowName'] = self::getFlow(['id' => $process['flow_id']])[0]['name'];
            $message_params['flowDescription'] = self::getFlow(['id' => $process['flow_id']])[0]['description'];
            $message_params['scope'] = $scope_map[$thread['scope']];
            $message_params['scopeName'] = $scope_name;
            $message_params['workerIds'] = implode(',', $worker_ids);
            $message_params['workerNames'] = self::getWorkerNameStr($worker_ids, $thread['scope']);
            $message_params['checkmode'] = $thread['checkmode'] == 1 ? '联合审批' : '任一审批';
            $message_params['stepName'] = self::getNodeById($process['flow_id'], $thread['step_id'])['data']['name'];
            $message_params['preStepName'] = self::getNodeById($process['flow_id'], $prestep_id)['data']['name'];
            $message_params['bill'] = $process['bill'];
            $message_params['billId'] = $process['bill_id'];
            $message_params['billName'] = self::getTableComment($process['bill']);
            $message_params['agency'] = $agency_info === false ? '否' : '是';
            $message_params['agentIds'] = $agency_info === false ? '' : implode(',', $agency_info['agent_ids']);
            $message_params['agentName'] = $agency_info === false ? '' : $agency_info['agent_name'];
            $message_params['principalIds'] = $agency_info === false ? '' : implode(',', $agency_info['principal_ids']);
            $message_params['principalName'] = $agency_info === false ? '' : $agency_info['principal_name'];
            if ($agency_info !== false) {
                foreach ($agency_info['agency_workers'] as $receiverId) {
                    $message_params['receivers'][] = ['id' => $receiverId, 'name' => self::getWorkerNameStr([$receiverId], $thread['scope'])];
                }
            }
            else {
                foreach ($worker_ids as $receiverId) {
                    $message_params['receivers'][] = ['id' => $receiverId, 'name' => self::getWorkerNameStr([$receiverId], $thread['scope'])];
                }
            }

        }
        elseif ($params['type'] == 'start') {
            $message_params['stepName'] = '发起流程';
            $message_params['bill'] = $params['bill'];
            $message_params['bill_id'] = $params['bill_id'];
            $message_params['bill_name'] = self::getTableComment($params['bill']);
            $initiator_id = self::getBillRow($params['bill'], $params['bill_id'])['initiator_id'];
            $agency_info = self::checkAgency($params['bill'], $params['bill_id'], 1, [$initiator_id]);
            if ($agency_info !== false) {
                $message_params['receivers'][] = ['id' => $agency_info['agent_ids'][0], 'name' => $agency_info['agent_name']];
            }
            else {
                $message_params['receivers'][] = ['id' => $initiator_id, 'name' => self::getWorkerNameStr([$initiator_id], 1)];
            }
            $message_params['agency'] = $agency_info === false ? '否' : '是';
            $message_params['agentIds'] = $agency_info === false ? '' : implode(',', $agency_info['agent_ids']);
            $message_params['agentName'] = $agency_info === false ? '' : $agency_info['agent_name'];
            $message_params['principalIds'] = $agency_info === false ? '' : implode(',', $agency_info['principal_ids']);
            $message_params['principalName'] = $agency_info === false ? '' : $agency_info['principal_name'];
        }
        elseif ($params['type'] == 'end') {
            $message_params['stepName'] = '流程已结束';
            $message_params['bill'] = $params['bill'];
            $message_params['bill_id'] = $params['bill_id'];
            $message_params['bill_name'] = self::getTableComment($params['bill']);
            $initiator_id = self::getBillRow($params['bill'], $params['bill_id'])['initiator_id'];
            $agency_info = self::checkAgency($params['bill'], $params['bill_id'], 1, [$initiator_id]);
            if ($agency_info !== false) {
                $message_params['receivers'][] = ['id' => $agency_info['agent_ids'][0], 'name' => $agency_info['agent_name']];
            }
            else {
                $message_params['receivers'][] = ['id' => $initiator_id, 'name' => self::getWorkerNameStr([$initiator_id], 1)];
            }
        }

        return $message_params;
    }


    public function sendMessage($params)
    {
        if ($this->msgObject != null && gettype($this->msgObject) == 'object') {
            $this->msgObject->sendMessage($params);
        }
    }

    public function addCarbon($param)
    {
        return carbonAdapter::callFn('addCarbon', $param);
    }


    public function getTableComment($table)
    {
        return toolAdapter::callFn('getTableComment', ['table' => $table]);
    }


    public function getSelectpageOperator($param)
    {
        $result = [];
        $operators = self::getOperator();

        if (!isset($param['keyValue'])) {
            foreach ($operators as $operator) {
                $result[] = [$param['keyField'] => $operator['value'], $param['showField'] => $operator['name']];
            }
        }
        else {
            $oplist = explode(',', $param['keyValue']);
            foreach ($oplist as $op) {
                for ($i = 0; $i < count($operators); $i++) {
                    if ($op == $operators[$i]['value']) {
                        $result[] = [$param['keyField'] => $operators[$i]['value'], $param['showField'] => $operators[$i]['name']];
                        break;
                    }
                }
            }
        }
        return ['total' => count($result), 'list' => $result];
    }


    public function getSessionUid()
    {
        $configSession = config::getConfig('session');
        $session = $_SESSION;
        foreach ($configSession['uid'] as $param) {
            $session = $session[$param];
        }
        return $session;
    }


    public function getUsers()
    {
        $users = workerAdapter::callFn('getUsers');
        return ['total' => count($users), 'list' => $users];
    }

    public function getUser($param)
    {
        return workerAdapter::callFn('getUser', $param);

    }


    public function getUserById($uid)
    {
        $users = workerAdapter::callFn('getUser', ['id' => $uid]);
        if ($users) {
            return $users[0];
        }
        return $users;
    }


    public function getSelectpageWorkers($param)
    {
        return workerAdapter::callFn('getSelectpageWorkers', $param);
    }


    public function getFieldsWithComment($table, $field = '')
    {
        $param = ['table' => $table, 'field' => $field];
        return toolAdapter::callFn('getFieldsWithComment', $param);
    }


    public function getSelectpageFieldsWithComment($param)
    {
        $result = [];
        $flow = self::getFlow(['id' => $param['flow_id']])[0];
        $columns = self::getFieldsWithComment($flow['bill']);

        if (!isset($param['keyValue'])) {
            foreach ($columns as $column) {
                $result[] = [$param['keyField'] => $column['field'], $param['showField'] => $column['field'] . '[' . $column['comment'] . ']'];
            }
        }
        else {
            $fields = explode(',', $param['keyValue']);
            foreach ($fields as $field) {
                for ($i = 0; $i < count($columns); $i++) {
                    if ($field == $columns[$i]['field']) {
                        $result[] = [$param['keyField'] => $columns[$i]['field'], $param['showField'] => $columns[$i]['field'] . '[' . $columns[$i]['comment'] . ']'];
                        break;
                    }
                }
            }
        }
        return ['total' => count($result), 'list' => $result];
    }


    public function getFlow($param)
    {
        $flow = flowAdapter::callFn('getFLow', $param);
        if ($flow) {
            foreach ($flow as &$item) {
                if ($item['graph'] == null) {
                    $item['graph'] = '';
                }
            }
            return $flow;
        }
        return false;

    }


    public function saveGraph($param)
    {
        return flowAdapter::callFn('saveGraph', $param);
    }


    public function getProcess($param)
    {
        return processAdapter::callFn('getProcess', $param);
    }


    public function setProcess($param)
    {
        return processAdapter::callFn('setProcess', $param);
    }


    public function getProcessStatus($bill, $bill_id)
    {
        $process = self::getProcess(['bill' => $bill, 'bill_id' => $bill_id]);
        if ($process) {
            return $process[0]['status'];
        }
        else {
            return $process;
        }
    }


    public function getThread($param)
    {
        return threadAdapter::callFn('getThread', $param);
    }


    public function setThread($param)
    {
        return threadAdapter::callFn('setThread', $param);
    }


    public function terminateProcess($process_id)
    {
        self::setProcess(['id' => $process_id, 'status' => 3]);
        $threads = self::getThread(['process_id' => $process_id, 'status' => 1]);
        if ($threads) {
            foreach ($threads as $thread) {
                self::setThread(['id' => $thread['id'], 'status' => 3]);
            }
        }
        $rows = lib::getProcess(['id' => $process_id]);
        if (!$rows) {
            return false;
        }
        $process = $rows[0];
        lib::addThreadLog([
            'bill' => $process['bill'],
            'bill_id' => $process['bill_id'],
            'step_name' => '强制终止',
            'worker_id' => self::getSessionUid(),
            'worker_name' => self::getUserById(self::getSessionUid())['username'],
            'check_type' => 4,
            'createtime' => (new \DateTime())->getTimestamp() + 1,
            'updatetime' => (new \DateTime())->getTimestamp() + 1,
        ]);
        lib::setBillStatus($process['bill'], $process['bill_id'], 3);
        return true;
    }


    public function delBill($bill, $bill_id)
    {
        if (is_array($bill_id)) {
            foreach ($bill_id as $id) {
                $process = self::getProcess(['bill' => $bill, 'bill_id' => $id]);
                if ($process) {
                    self::terminateProcess($process[0]['id']);
                }
            }
        }
        else {
            $process = self::getProcess(['bill' => $bill, 'bill_id' => $bill_id]);
            if ($process) {
                self::terminateProcess($process[0]['id']);
            }
        }

    }


    public function endProcess($process_id)
    {
        self::setProcess(['id' => $process_id, 'status' => 2]);
        $threads = self::getThread(['process_id' => $process_id, 'status' => 1]);
        if ($threads) {
            foreach ($threads as $thread) {
                self::setThread(['id' => $thread['id'], 'status' => 3]);
            }
        }
    }


    public function getWorkerNameStr($worker_ids, $scope)
    {
        $worker_name = '';
        if ($scope == 1 || $scope == 3) {
            foreach ($worker_ids as $worker_id) {
                $uid = self::getUserById($worker_id);
                if ($uid) {
                    $user_name = self::getUserById($worker_id)['username'];
                    $worker_name .= $user_name . ',';
                }
            }
        }
        elseif ($scope == 2 || $scope == 4 || $scope == 5) {
            foreach ($worker_ids as $worker_id) {
                $group = self::getGroup($worker_id);
                if ($group) {
                    $group_name = self::getGroup($worker_id)[0]['groupname'];
                    $worker_name .= $group_name . ',';
                }
            }
        }

        return rtrim($worker_name, ',');
    }


    public function getWorkerNameList($worker_ids, $scope)
    {
        $worker_name = [];
        if ($scope == 1 || $scope == 3) {
            foreach ($worker_ids as $worker_id) {
                $uid = self::getUserById($worker_id);
                if ($uid) {
                    $user_name = self::getUserById($worker_id)['username'];
                    $worker_name[] = $user_name;
                }
            }
        }
        elseif ($scope == 2 || $scope == 4 || $scope == 5) {
            foreach ($worker_ids as $worker_id) {
                $group = self::getGroup($worker_id);
                if ($group) {
                    $group_name = self::getGroup($worker_id)[0]['groupname'];
                    $worker_name [] = $group_name;
                }
            }
        }

        return $worker_name;
    }


    public function getThreadStepName($thread_id)
    {
        $thread = self::getThread(['id' => $thread_id])[0];
        $step_id = $thread['step_id'];
        $process_id = $thread['process_id'];
        $process = self::getProcess(['id' => $process_id])[0];
        $step = self::getNodeById($process['flow_id'], $step_id);
        if (!$step) {
            return '';
        }
        return $step['data']['name'];
    }


    public function getCanFlowTargetStep($flow_id, $node, $bill, $bill_id)
    {
        $canrun_target_step = [];
        $targets = self::getTargetNodes($flow_id, $node);
        foreach ($targets as $target) {
            if ($target['data']['type'] == 'condition') {
                $steps = self::getConditionCanRunStep($flow_id, $bill, $bill_id, $target);
                foreach ($steps as $step) {
                    $canrun_target_step[] = $step;
                }
            }
            else {
                $canrun_target_step[] = $target;
            }
        }
        return $canrun_target_step;
    }


    public function getThreadCanFlowTargetStepNameList($thread)
    {
        $process = self::getProcess(['id' => $thread['process_id']])[0];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];
        $flow_id = $process['flow_id'];
        $node = self::getNodeById($flow_id, $thread['step_id']);
        $can_flow_steps = self::getCanFlowTargetStep($flow_id, $node, $bill, $bill_id);
        $step_names = [];
        foreach ($can_flow_steps as $step) {
            $step_names[] = $step['data']['name'];
        }
        return $step_names;
    }


    public function getRunThreadInfo($bill, $bill_id)
    {
        $processList = self::getProcess(['bill' => $bill, 'bill_id' => $bill_id]);
        if (!$processList) {
            return -1;
        }
        $process = $processList[0];
        $flow_id = $process['flow_id'];
        $flows = self::getFlow(['id' => $flow_id]);
        if (!$flows) {
            return 2;
        }
        $flow = $flows[0];
        $threads = self::getThread(['process_id' => $process['id'], 'status' => 1]);
        $thread_info = [];
        if ($threads) {
            foreach ($threads as $thread) {
                $step_data = self::getNodeData($flow['id'], $thread['step_id']);
                $thread_info[] = [
                    'id' => $thread['id'],
                    'name' => isset($step_data['name']) ? $step_data['name'] : '',
                    'check_worker_str' => self::getWorkerNameStr(self::getWorkerIds($bill, $bill_id, $thread), $thread['scope']),
                    'check_worker_list' => self::getWorkerNameList(self::getWorkerIds($bill, $bill_id, $thread), $thread['scope']),
                    'checked_worker_str' => self::getWorkerNameStr(explode(',', $thread['worker_checked_ids']), $thread['scope']),
                    'checked_worker_list' => self::getWorkerNameList(explode(',', $thread['worker_checked_ids']), $thread['scope']),
                    'checkmode' => $thread['checkmode'],
                    'is_sign' => $thread['is_sign'],
                    'is_back' => $thread['is_back'],
                    'can_back' => $thread['can_back'],
                    'can_sign' => $thread['can_sign'],
                    'pre_step' => self::getThreadStepName($thread['prethread_id']),
                    'next_step' => implode(',', self::getThreadCanFlowTargetStepNameList($thread)),
                    'auth_info' => self::checkThreadAuth($thread),
                    'agency_info' => self::getThreadAgencyInfo($thread),
                ];
            }
        }
        else {
            return 2;
        }
        return $thread_info;
    }


    public function getBillCanCheckThreadCount($bill)
    {
        $bill_rows_first = self::getBillRows($bill, ['status' => 0]);
        $bill_rows_second = self::getBillRows($bill, ['status' => 1]);
        $bill_rows = [];
        if ($bill_rows_first && $bill_rows_second) {
            $bill_rows = array_merge($bill_rows_first, $bill_rows_second);
        }
        else {
            $bill_rows = $bill_rows_first ? $bill_rows_first : $bill_rows_second;
        }

        $cancheck_threads_count = 0;
        if ($bill_rows) {
            foreach ($bill_rows as $bill_row) {
                $process = self::getProcess(['bill' => $bill, 'bill_id' => $bill_row['id']]);
                if ($process) {
                    $threads = self::getThread(['process_id' => $process[0]['id'], 'status' => 1]);
                    if ($threads) {
                        foreach ($threads as $thread) {
                            if (self::checkThreadAuth($thread) !== false) {
                                $cancheck_threads_count += 1;
                            }
                        }
                    }
                }
                else {
                    $code = self::getBillAuthInfo($bill_row['initiator_id'], $bill, $bill_row['id'])['code'];
                    if ($code == 0 || $code == 1) {
                        $cancheck_threads_count += 1;
                    }
                }
            }
        }

        return $cancheck_threads_count;
    }


    public function getNodeData($flow_id, $node_id)
    {
        $flow = self::getFlow(['id' => $flow_id])[0];
        $cells = $flow['graph']['cells'];
        $node_data = null;
        for ($i = 0; $i < count($cells); $i++) {
            if ($cells[$i]['id'] == $node_id) {
                $node_data = $cells[$i]['data'];
                break;
            }
        }
        return $node_data;
    }


    public function getUserGroupIds($uid)
    {
        $group_access_config = config::getConfig('group_access');
        $group_id_column = $group_access_config['group_id'];
        $group_ids = [];
        $group_access = workerAdapter::callFn('getGroupAccess', [$group_access_config['uid'] => $uid]);
        if (!$group_access) {
            return false;
        }
        foreach ($group_access as $access) {
            $group_arr = explode(',', $access[$group_id_column]);
            foreach ($group_arr as $group_id) {
                $group_ids[] = $group_id;
            }
        }
        return $group_ids;
    }

    public function getGroupUserIds($group_id)
    {
        $group_access_config = config::getConfig('group_access');
        $group_id_column = $group_access_config['group_id'];
        $user_ids = [];
        $group_access = workerAdapter::callFn('getGroupAccess', [$group_id_column => $group_id]);
        if (!$group_access) {
            return false;
        }
        foreach ($group_access as $access) {
            $user_ids[] = $access[$group_access_config['uid']];
        }
        return $user_ids;
    }


    public function getBillRow($bill, $bill_id)
    {
        $param = ['bill' => $bill, 'where' => ['id' => $bill_id]];
        $rows = toolAdapter::callFn('getBillRows', $param);
        if ($rows) {
            return $rows[0];
        }
        return false;
    }


    public function getBillRows($bill, $where = '')
    {
        $param = ['bill' => $bill, 'where' => $where];
        $rows = toolAdapter::callFn('getBillRows', $param);
        return $rows;
    }


    public function getGroup($group_id)
    {
        $param = ['id' => $group_id];
        return workerAdapter::callFn('getGroup', $param);

    }


    public function getAgency($param)
    {
        return agencyAdapter::callFn('getAgency', $param);
    }


    public function checkAgency($bill, $bill_id, $scope, $workers)
    {
        $priority = [3, 2, 1];
        $scope_map = [1 => 1, 2 => 2, 3 => 1, 4 => 2, 5 => 2];
        $agency_workers = $workers;
        $agenct_ids = [];
        $principal_ids = [];
        foreach ($priority as $range) {
            if ($range == 1) {
                $agency_rows = self::getAgency(['range' => $range, 'bill' => $bill, 'bill_id' => $bill_id, 'scope' => $scope_map[$scope]]);
            }
            elseif ($range == 2) {
                $agency_rows = self::getAgency(['range' => $range, 'bill' => $bill, 'scope' => $scope_map[$scope]]);
            }
            elseif ($range == 3) {
                $agency_rows = self::getAgency(['range' => $range, 'scope' => $scope_map[$scope]]);
            }
            if ($agency_rows) {
                foreach ($workers as $key => $worker) {
                    for ($i = 0; $i < count($agency_rows); $i++) {
                        if ($worker == $agency_rows[$i]['principal_id']) {
                            $agency_workers[$key] = $agency_rows[$i]['agent_id'];
                            $principal_ids[$key] = $worker;
                            $agenct_ids[] = $agency_rows[$i]['agent_id'];
                            break;
                        }
                    }
                }
            }
        }
        if ($agency_workers != $workers) {
            $agency_info = [
                'agency_workers' => array_unique($agency_workers),
                'range' => $range,
                'scope' => $scope,
                'agent_ids' => $agenct_ids,
                'agent_name' => self::getWorkerNameStr($agenct_ids, $scope),
                'principal_ids' => $principal_ids,
                'principal_name' => self::getWorkerNameStr($principal_ids, $scope)
            ];
            return $agency_info;
        }

        return false;

    }


    public function getThreadAgencyInfo($thread)
    {
        $scope = $thread['scope'];
        $workers = explode(',', $thread['worker_ids']);
        $uid = self::getSessionUid();

        $process = self::getProcess(['id' => $thread['process_id']])[0];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];

        $agency_info = null;

        if ($scope == 1) {
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $workers = array_diff($workers, $worker_checked_ids);
            $agency = self::checkAgency($bill, $bill_id, $scope, $workers);
            if ($agency !== false) {
                $agency_info = $agency;
            }
        }
        elseif ($scope == 2) {
            $user_group_ids = self::getUserGroupIds($uid);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $workers = array_diff($workers, $worker_checked_ids);
            $agency = self::checkAgency($bill, $bill_id, $scope, $workers);
            if ($agency !== false) {
                $agency_info = $agency;
            }
        }
        elseif ($scope == 3) {
            $bill_workers = self::getBillWorker($bill, $bill_id, $workers);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $bill_workers = array_diff($bill_workers, $worker_checked_ids);
            $agency = self::checkAgency($bill, $bill_id, $scope, $bill_workers);
            if ($agency !== false) {
                $agency_info = $agency;
            }
        }
        elseif ($scope == 4) {
            $bill_workers = self::getBillWorker($bill, $bill_id, $workers);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $bill_workers = array_diff($bill_workers, $worker_checked_ids);
            $user_group_ids = self::getUserGroupIds($uid);
            $agency = self::checkAgency($bill, $bill_id, $scope, $bill_workers);
            if ($agency !== false) {
                $agency_info = $agency;
            }
        }
        elseif ($scope == 5) {
            $bill_row = self::getBillRow($bill, $bill_id);
            if ($bill_row) {
                $initiator_id = $bill_row['initiator_id'];
                $initiator_group_ids = self::getUserGroupIds($initiator_id);
                $user_group_ids = self::getUserGroupIds($uid);
                $initiator_group_pids = [];
                foreach ($initiator_group_ids as $initiator_group_id) {
                    $initiator_group_pids[] = self::getGroup($initiator_group_id)[0]['pid'];
                }

                $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
                $initiator_group_pids = array_diff($initiator_group_pids, $worker_checked_ids);
                $agency = self::checkAgency($bill, $bill_id, $scope, $initiator_group_pids);
                if ($agency !== false) {
                    $agency_info = $agency;
                }
            }

        }

        return $agency_info;
    }

    public function checkThreadAuth($thread)
    {
        $scope = $thread['scope'];
        $workers = explode(',', $thread['worker_ids']);
        $uid = self::getSessionUid();

        $process = self::getProcess(['id' => $thread['process_id']])[0];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];

        $auth_info = ['auth' => true, 'agency' => false, 'agency_info' => null];

        if ($scope == 1) {
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $workers = array_diff($workers, $worker_checked_ids);
            $agency = self::checkAgency($bill, $bill_id, $scope, $workers);
            if ($agency !== false) {
                $workers = $agency['agency_workers'];
                $auth_info['agency'] = true;
                $auth_info['agency_info'] = $agency;
            }
            if (in_array($uid, $workers)) {
                $auth_info['auth'] = true;
                return $auth_info;
            }
        }
        elseif ($scope == 2) {
            $user_group_ids = self::getUserGroupIds($uid);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $workers = array_diff($workers, $worker_checked_ids);
            $agency = self::checkAgency($bill, $bill_id, $scope, $workers);
            if ($agency !== false) {
                $workers = $agency['agency_workers'];
                $auth_info['agency'] = true;
                $auth_info['agency_info'] = $agency;
            }
            foreach ($user_group_ids as $group_id) {
                if (in_array($group_id, $workers)) {
                    $auth_info['auth'] = true;
                    return $auth_info;
                }
            }
        }
        elseif ($scope == 3) {
            $bill_workers = self::getBillWorker($bill, $bill_id, $workers);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $bill_workers = array_diff($bill_workers, $worker_checked_ids);
            $agency = self::checkAgency($bill, $bill_id, $scope, $bill_workers);
            if ($agency !== false) {
                $bill_workers = $agency['agency_workers'];
                $auth_info['agency'] = true;
                $auth_info['agency_info'] = $agency;
            }
            if (in_array($uid, $bill_workers)) {
                $auth_info['auth'] = true;
                return $auth_info;
            }

        }
        elseif ($scope == 4) {
            $bill_workers = self::getBillWorker($bill, $bill_id, $workers);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $bill_workers = array_diff($bill_workers, $worker_checked_ids);
            $user_group_ids = self::getUserGroupIds($uid);
            $agency = self::checkAgency($bill, $bill_id, $scope, $bill_workers);
            if ($agency !== false) {
                $bill_workers = $agency['agency_workers'];
                $auth_info['agency'] = true;
                $auth_info['agency_info'] = $agency;
            }
            foreach ($user_group_ids as $user_group_id) {
                if (in_array($user_group_id, $bill_workers)) {
                    $auth_info['auth'] = true;
                    return $auth_info;
                }
            }
        }
        elseif ($scope == 5) {
            $bill_row = self::getBillRow($bill, $bill_id);
            if ($bill_row) {
                $initiator_id = $bill_row['initiator_id'];
                $initiator_group_ids = self::getUserGroupIds($initiator_id);
                $user_group_ids = self::getUserGroupIds($uid);
                $initiator_group_pids = [];
                foreach ($initiator_group_ids as $initiator_group_id) {
                    $initiator_group_pids[] = self::getGroup($initiator_group_id)[0]['pid'];
                }

                $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
                $initiator_group_pids = array_diff($initiator_group_pids, $worker_checked_ids);
                $agency = self::checkAgency($bill, $bill_id, $scope, $initiator_group_pids);
                if ($agency !== false) {
                    $initiator_group_pids = $agency['agency_workers'];
                    $auth_info['agency'] = true;
                    $auth_info['agency_info'] = $agency;
                }

                $common_ids = array_intersect($user_group_ids, $initiator_group_pids);
                if (count($common_ids) > 0) {
                    $auth_info['auth'] = true;
                    return $auth_info;
                }
            }

        }

        return false;
    }


    public function getBillAuthInfo($createuser_id, $bill, $bill_id)
    {
        $process = self::getProcess(['bill' => $bill, 'bill_id' => $bill_id]);
        if ($process) {
            if ($process[0]['status'] == 1) {
                $threads = self::getThread(['process_id' => $process[0]['id'], 'status' => 1]);
                if ($threads) {
                    $can_checks = [];
                    foreach ($threads as $thread) {
                        $thread_auth = self::checkThreadAuth($thread);
                        if ($thread_auth !== false) {
                            $can_checks[] = $thread;
                        }
                    }
                    if (count($can_checks) > 0) {
                        return ['code' => 1, 'msg' => '待处理', 'can_checks' => $can_checks];
                    }
                    else {
                        return ['code' => -1, 'msg' => '无权限'];
                    }
                }
                else {
                    return ['code' => -3, 'msg' => '流程故障'];
                }
            }
            elseif ($process[0]['status'] == 2) {
                return ['code' => 2, 'msg' => '已结束'];
            }
            elseif ($process[0]['status'] == 3) {
                return ['code' => 3, 'msg' => '已终止'];
            }

        }
        elseif ($process == 0) {
            $uid = self::getSessionUid();
            if ($uid != $createuser_id) {
                return ['code' => -2, 'msg' => '等待创建人发起'];
            }
            else {
                return ['code' => 0, 'msg' => '发起申请'];
            }
        }

    }


    public function getFlowProgress($bill, $bill_id)
    {
        $process = self::getProcess(['bill' => $bill, 'bill_id' => $bill_id]);
        if (!$process) {
            return 0;
        }
        $process = $process[0];
        if ($process['status'] == 2 || $process['status'] == 3) {
            return 1;
        }
        $flow_id = $process['flow_id'];
        $start_node = self::getNodeByType($flow_id, 'start')[0];
        $end_node = self::getNodeByType($flow_id, 'end')[0];
        $must_check_path = self::getAllMustCheckPath($flow_id, $process['id'], $start_node, $end_node);
        $must_check_path_without_condition = [];

        foreach ($must_check_path as $path_key => $path) {
            foreach ($path as $node_id) {
                if (self::getNodeById($flow_id, $node_id)['data']['type'] != 'condition') {
                    $must_check_path_without_condition[$path_key][] = $node_id;
                }
            }
        }
        $running_threads = self::getThread(['process_id' => $process['id'], 'status' => 1]);
        $max_progress = 0;
        if (!$running_threads) {
            return 0;
        }
        foreach ($running_threads as $running_thread) {
            for ($i = 0; $i < count($must_check_path_without_condition); $i++) {
                for ($k = 0; $k < count($must_check_path_without_condition[$i]); $k++) {
                    if ($running_thread['step_id'] == $must_check_path_without_condition[$i][$k]) {
                        $branch_progress = $k / count($must_check_path_without_condition[$i]);
                        $max_progress = $branch_progress > $max_progress ? $branch_progress : $max_progress;
                        break;
                    }
                }
            }
        }
        return $max_progress;
    }


    public function addProcess($param)
    {
        return processAdapter::callFn('addProcess', $param);
    }


    public function addThread($param)
    {
        return threadAdapter::callFn('addThread', $param);
    }


    public function getAllCells($flow_id)
    {
        $flows = self::getFlow(['id' => $flow_id]);
        if (!$flows) {
            return false;
        }
        $flow = $flows[0];
        if ($flow['graph'] == '') {
            return false;
        }
        if (!isset($flow['graph']['cells'])) {
            return false;
        }
        return $flow['graph']['cells'];
    }


    public function getAllNodes($flow_id)
    {
        $cells = self::getAllCells($flow_id);
        if (!$cells){
            return false;
        }
        $nodes = [];
        foreach ($cells as $cell) {
            if (isset($cell['data'])) {
                $nodes[] = $cell;
            }
        }
        return $nodes;
    }


    public function getAllStep($flow_id)
    {
        $cells = self::getAllCells($flow_id);
        if(!$cells){
            return [];
        }
        $steps = [];
        foreach ($cells as $cell) {
            if (isset($cell['data'])) {
                if ($cell['data']['type'] == 'start' || $cell['data']['type'] == 'end' || $cell['data']['type'] == 'step') {
                    $steps[] = $cell;
                }
            }
        }
        return $steps;
    }


    public function getNodeByType($flow_id, $type)
    {
        $cells = self::getAllCells($flow_id);
        if (!$cells) {
            return false;
        }
        $nodes = [];
        foreach ($cells as $cell) {
            if (isset($cell['data'])) {
                if ($cell['data']['type'] == $type) {
                    $nodes[] = $cell;
                }
            }
        }
        return $nodes;
    }


    public function getNodeById($flow_id, $id)
    {
        $cells = self::getAllCells($flow_id);
        if (!$cells) {
            return false;
        }
        foreach ($cells as $cell) {
            if ($cell['id'] == $id) {
                return $cell;
            }
        }
        return false;
    }


    public function getTargetNodes($flow_id, $node)
    {
        $cells = self::getAllCells($flow_id);
        if (!$cells) {
            return false;
        }
        $target_ids = [];
        foreach ($cells as $cell) {
            if ($cell['shape'] == 'edge') {
                if ($cell['source']['cell'] == $node['id']) {
                    $target_ids[] = $cell['target']['cell'];
                }
            }
        }
        $targets = [];
        foreach ($target_ids as $target_id) {
            foreach ($cells as $cell) {
                if ($cell['id'] == $target_id) {
                    $targets[] = $cell;
                }
            }
        }
        return $targets;
    }


    public function getSourceNodes($flow_id, $node)
    {
        $cells = self::getAllCells($flow_id);
        $source_ids = [];
        foreach ($cells as $cell) {
            if ($cell['shape'] == 'edge') {
                if ($cell['target']['cell'] == $node['id']) {
                    $source_ids[] = $cell['source']['cell'];
                }
            }
        }
        $sources = [];
        foreach ($source_ids as $target_id) {
            foreach ($cells as $cell) {
                if ($cell['id'] == $target_id) {
                    $sources[] = $cell;
                }
            }
        }
        return $sources;
    }


    public function addThreadLog($param)
    {
        return logAdapter::callFn('addThreadLog', $param);
    }


    public function getAllPath($flow_id, $start_node, $end_node)
    {
        $matrix = [];
        $allNodes = self::getAllNodes($flow_id);
        if (!$allNodes){
            return [];
        }
        foreach ($allNodes as $node) {
            $targets = self::getTargetNodes($flow_id, $node);
            $targets_id = [];
            foreach ($targets as $target) {
                $targets_id[] = $target['id'];
            }
            $matrix[$node['id']] = $targets_id;
        }
        $graph = new graph();
        $paths = $graph->getAllPath($matrix, $start_node['id'], $end_node['id']);
        return $paths;
    }


    public function getCommonSourceNode($flow_id, $nodes)
    {
        $paths = [];
        $start_node = self::getNodeByType($flow_id, 'start')[0];
        foreach ($nodes as $node) {
            $path = self::getAllPath($flow_id, $start_node, $node);
            $paths[] = $path;
        }
        $common_temp = [];
        foreach ($paths[0] as $first) {
            for ($i = 1; $i < count($paths); $i++) {
                for ($j = 0; $j < count($paths[$i]); $j++) {
                    $temp = array_intersect($first, $paths[$i][$j]);
                    if (count($temp) > 0) {
                        $common_temp[] = $temp;
                        break;
                    }
                }
            }
        }
        if (count($common_temp) > 1) {
            $common_temp = call_user_func_array('array_intersect', $common_temp);
        }
        else {
            $common_temp = $common_temp[0];
        }
        $common_node_id = end($common_temp);
        $common_node = self::getNodeById($flow_id, $common_node_id);
        return $common_node;
    }


    public function getCommonDestinationNode($flow_id, $nodes)
    {
        $paths = [];
        $end_node = self::getNodeByType($flow_id, 'end')[0];
        foreach ($nodes as $node) {
            $path = self::getAllPath($flow_id, $node, $end_node);
            $paths[] = $path;
        }
        $common_temp = [];
        foreach ($paths[0] as $first) {
            for ($i = 1; $i < count($paths); $i++) {
                for ($j = 0; $j < count($paths[$i]); $j++) {
                    $temp = array_intersect($first, $paths[$i][$j]);
                    if (count($temp) > 0) {
                        $common_temp[] = $temp;
                        break;
                    }
                }
            }
        }
        if (count($common_temp) > 1) {
            $common_temp = call_user_func_array('array_intersect', $common_temp);
        }
        else {
            $common_temp = $common_temp[0];
        }
        $common_node_id = reset($common_temp);
        $common_node = self::getNodeById($flow_id, $common_node_id);
        return $common_node;
    }


    public function getCombination($arr, $num)
    {
        if ($num == 1) {
            return $arr;
        }
        if ($num > count($arr)) {
            return [];
        }
        $combination = [];
        $arr_temp = $arr;

        for ($i = 0; $i < count($arr); $i++) {
            if (count($arr) - $i < $num) {
                break;
            }
            $origin = $arr[$i];
            array_shift($arr_temp);
            $branchs = self::getCombination($arr_temp, $num - 1);
            foreach ($branchs as $branch) {
                $combination[] = $origin . ',' . $branch;
            }
        }
        return $combination;
    }


    public function endThread($thread)
    {
        $thread['status'] = 2;
        self::setThread($thread);
    }


    public function nodeListToIdList($nodes)
    {
        $id_list = [];
        foreach ($nodes as $node) {
            $id_list[] = $node['id'];
        }
        return $id_list;
    }


    public function idListToNodeList($flow_id, $ids)
    {
        $nodes = [];
        foreach ($ids as $id) {
            $nodes[] = self::getNodeById($flow_id, $id);
        }
        return $nodes;
    }


    public function buildFlowFormula($flow_id, $start_node, $end_node, $sources)
    {
        $result = null;
        $targets = self::getTargetNodes($flow_id, $start_node);
        $mode = 1;
        if (count($targets) > 1) {
            if ($start_node['data']['type'] == 'start' || $start_node['data']['type'] == 'step') {
                $result = ['and' => []];
                $mode = 1;
            }
            elseif ($start_node['data']['type'] == 'condition') {
                $result = ['or' => []];
                $mode = 2;
            }
            foreach ($targets as $target) {
                if (in_array($target, $sources)) {
                    if ($mode == 1) {
                        $result['and'][] = $target['id'];
                    }
                    else {
                        $result['or'][] = $target['id'];
                    }

                }
                else {
                    if ($mode == 1) {
                        $result['and'][] = self::buildFlowFormula($flow_id, $target, $end_node, $sources);
                    }
                    elseif ($mode == 2) {
                        $result['or'][] = self::buildFlowFormula($flow_id, $target, $end_node, $sources);
                    }
                }


            }
        }
        if (count($targets) == 1 && $targets[0] != $end_node) {
            $result = self::buildFlowFormula($flow_id, $targets[0], $end_node, $sources);
        }
        if (count($targets) == 1 && $targets[0] == $end_node) {
            return $start_node['id'];
        }

        return $result;
    }


    public function checkFlowFormula($process_id, $formula)
    {
        $and_or_flag = 1;
        $or_temp = [];
        $and_temp = [];
        if (isset($formula['and'])) {
            $and_or_flag = 1;
            $formula_items = $formula['and'];
        }
        elseif (isset($formula['or'])) {
            $and_or_flag = 2;
            $formula_items = $formula['or'];
        }
        foreach ($formula_items as $item) {
            if (is_string($item)) {

                $threads = self::getThread(['process_id' => $process_id, 'step_id' => $item]);
                if ($threads) {
                    $thread_flag = true;
                    foreach ($threads as $thread) {
                        if ($thread['status'] == 1) {
                            $thread_flag = false;
                        }
                    }
                }
                else {
                    $thread_flag = false;
                }
                if ($and_or_flag == 1) {
                    $and_temp[] = $thread_flag;
                }
                elseif ($and_or_flag == 2) {
                    $or_temp[] = $thread_flag;
                }

            }
            else {
                if ($and_or_flag == 1) {
                    $and_temp[] = self::checkFlowFormula($process_id, $item);
                }
                elseif ($and_or_flag == 2) {
                    $or_temp[] = self::checkFlowFormula($process_id, $item);
                }

            }

        }

        if ($and_or_flag == 1) {
            $result = true;
            foreach ($and_temp as $and_item) {
                if (!$and_item) {
                    $result = false;
                }
            }
        }
        elseif ($and_or_flag == 2) {
            $result = false;
            foreach ($or_temp as $or_item) {
                if ($or_item) {
                    $result = true;
                }
            }
        }
        return $result;
    }


    public function getAllMustCheckSteps($flow_id, $process_id, $start_node, $end_node, $pre_must_check_steps = [])
    {
        if ($start_node == $end_node) {
            return [];
        }
        $must_check_steps = [];
        $targets = self::getTargetNodes($flow_id, $start_node);
        if ($start_node['data']['type'] == 'step' || $start_node['data']['type'] == 'start') {
            foreach ($targets as $target) {
                if ($target['data']['type'] == 'step') {
                    if ($target != $end_node) {
                        $must_check_steps[] = $target;
                    }
                }
                if (!in_array($target, $pre_must_check_steps)) {
                    $pre_must_check_steps = array_merge($pre_must_check_steps, $must_check_steps);
                    $steps_temp = self::getAllMustCheckSteps($flow_id, $process_id, $target, $end_node, $pre_must_check_steps);
                    foreach ($steps_temp as $step) {
                        $must_check_steps[] = $step;
                    }
                }
            }
        }
        elseif ($start_node['data']['type'] == 'condition') {
            $condition_log_rows = self::getConditionLog($process_id, $start_node['id']);
            if (!$condition_log_rows) {
                foreach ($targets as $target) {
                    if ($target['data']['type'] == 'step') {
                        if ($target != $end_node) {
                            $must_check_steps[] = $target;
                        }
                    }
                    if (!in_array($target, $pre_must_check_steps)) {
                        $pre_must_check_steps = array_merge($pre_must_check_steps, $must_check_steps);
                        $steps_temp = self::getAllMustCheckSteps($flow_id, $process_id, $target, $end_node, $pre_must_check_steps);
                        foreach ($steps_temp as $step) {
                            $must_check_steps[] = $step;
                        }
                    }
                }
            }
            else {
                foreach ($condition_log_rows as $row) {
                    $node = self::getNodeById($flow_id, $row['node_id']);
                    if ($node['data']['type'] == 'step') {
                        if ($node != $end_node) {
                            $must_check_steps[] = $node;
                        }
                    }

                    if (!in_array($node, $pre_must_check_steps)) {
                        $pre_must_check_steps = array_merge($pre_must_check_steps, $must_check_steps);
                        $steps_temp = self::getAllMustCheckSteps($flow_id, $process_id, $node, $end_node, $pre_must_check_steps);
                        foreach ($steps_temp as $step) {
                            $must_check_steps[] = $step;
                        }
                    }
                }
            }
        }
        return array_unique($must_check_steps, SORT_REGULAR);
    }


    public function getAllMustCheckPath($flow_id, $process_id, $start_node, $end_node)
    {
        $all_must_check_steps = self::getAllMustCheckSteps($flow_id, $process_id, $start_node, $end_node);
        $all_must_check_step_ids = [];
        foreach ($all_must_check_steps as $must_check_step) {
            $all_must_check_step_ids[] = $must_check_step['id'];
        }
        $all_path = self::getAllPath($flow_id, $start_node, $end_node);
        $all_must_check_path = [];
        foreach ($all_path as &$path) {
            array_pop($path);
            array_shift($path);
            $must_check = true;
            foreach ($path as $key => $node_id) {
                if (self::getNodeById($flow_id, $node_id)['data']['type'] != 'condition' && !in_array($node_id, $all_must_check_step_ids)) {
                    $must_check = false;
                }
            }
            if ($must_check) {
                $all_must_check_path[] = $path;
            }
        }
        return $all_must_check_path;
    }


    public function checkWhetherCanFLow($flow_id, $process_id, $node)
    {

        $canflow = true;
        $targets = self::getTargetNodes($flow_id, $node);
        if (count($targets) == 1) {
            $sources = self::getSourceNodes($flow_id, $targets[0]);
            if (count($sources) > 1) {
                $commonsource = self::getCommonSourceNode($flow_id, $sources);
                $all_must_check_steps = self::getAllMustCheckSteps($flow_id, $process_id, $commonsource, $targets[0]);
                $must_checks = [];
                foreach ($sources as $source) {
                    if (in_array($source, $all_must_check_steps)) {
                        $must_checks[] = $source;
                    }
                }
                foreach ($must_checks as $step) {

                    $threads = self::getThread(['process_id' => $process_id, 'step_id' => $step['id']]);
                    if ($threads) {
                        foreach ($threads as $thread) {
                            if ($thread['status'] == 1) {
                                $canflow = false;
                            }
                        }
                    }
                    else {
                        $canflow = false;
                    }
                }
            }

        }
        return $canflow;
    }


    public function getTargetSteps($flow_id, $node)
    {
        $steps = [];
        $targets = self::getTargetNodes($flow_id, $node);
        foreach ($targets as $target) {
            if ($target['data']['type'] == 'condition') {
                foreach (self::getTargetSteps($flow_id, $target) as $step) {
                    $steps[] = $step;
                }
            }
            else {
                $steps[] = $target;
            }
        }
        return $steps;
    }


    public function getDynamicSteps($flow_id, $node, $bill, $bill_id)
    {
        $dynamic_steps = [];
        $target_nodes = self::getTargetNodes($flow_id, $node);
        foreach ($target_nodes as $target_node) {
            if ($target_node['data']['type'] == 'condition') {
                $steps = self::getConditionCanRunStep($flow_id, $bill, $bill_id, $target_node);
                foreach ($steps as $step) {
                    if (isset($step['data']['scope'])) {
                        if ($step['data']['scope'] == 6) {
                            $dynamic_steps[] = $step;
                        }
                    }
                }
            }
            elseif ($target_node['data']['type'] == 'step') {
                if (isset($target_node['data']['scope'])) {
                    if ($target_node['data']['scope'] == 6) {
                        $dynamic_steps[] = $target_node;
                    }
                }
            }

        }
        return $dynamic_steps;
    }


    public function checkBillValue($bill, $bill_id, $column, $operator, $value)
    {
        $param = ['bill' => $bill, 'bill_id' => $bill_id, 'column' => $column, 'operator' => $operator, 'value' => $value];
        $result = toolAdapter::callFn('getBillByCondition', $param);
        if (!$result) {
            return false;
        }
        return true;
    }

    public function getConditionCanRunStep($flow_id, $bill, $bill_id, $condition_node)
    {
        $flowoutcondition = json_decode($condition_node['data']['flowoutcondition'], true);
        $result = [];
        foreach ($flowoutcondition as $condition) {
            if ($condition['column'] == '' || $condition['operator'] == '' || $condition['value'] == '') {
                return [];
            }
            if (self::checkBillValue($bill, $bill_id, $condition['column'], $condition['operator'], $condition['value'])) {
                $node = self::getNodeById($flow_id, $condition['step']);
                if ($node['data']['type'] == 'step' || $node['data']['type'] == 'end') {
                    $result[] = $node;
                }
                elseif ($node['data']['type'] == 'condition') {
                    $canrun_nodes = self::getConditionCanRunStep($flow_id, $bill, $bill_id, $node);
                    foreach ($canrun_nodes as $canrun_node) {
                        $result[] = $canrun_node;
                    }
                }
            }
        }
        return $result;
    }


    public function addConditionLog($process_id, $condition_id, $node_id)
    {
        return logAdapter::callFn('addConditionLog', ['process_id' => $process_id, 'condition_id' => $condition_id, 'node_id' => $node_id]);
    }


    public function getConditionLog($process_id, $condition_id)
    {
        return logAdapter::callFn('getConditionLog', ['process_id' => $process_id, 'condition_id' => $condition_id]);
    }


    public function addConditionCanRunLog($process_id, $condition_node)
    {
        $process = self::getProcess(['id' => $process_id])[0];
        $flow_id = $process['flow_id'];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];
        $flowoutcondition = json_decode($condition_node['data']['flowoutcondition'], true);
        foreach ($flowoutcondition as $condition) {
            if ($condition['column'] == '' || $condition['operator'] == '' || $condition['value'] == '') {
                return false;
            }
            if (self::checkBillValue($bill, $bill_id, $condition['column'], $condition['operator'], $condition['value'])) {
                self::addConditionLog($process_id, $condition_node['id'], $condition['step']);
                $node = self::getNodeById($flow_id, $condition['step']);
                if ($node['data']['type'] == 'condition') {
                    self::addConditionCanRunLog($process_id, $node);
                }
            }
        }
    }


    public function startTargetsThread($param)
    {
        $thread = self::getThread(['id' => $param['thread_id']])[0];
        $process_id = $thread['process_id'];
        $process = self::getProcess(['id' => $process_id])[0];
        $flow_id = $process['flow_id'];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];
        $node = self::getNodeById($flow_id, $thread['step_id']);
        $targets = self::getTargetNodes($flow_id, $node);
        foreach ($targets as $target) {
            if ($target['data']['type'] == 'end') {
                $newThread_id = self::addThread(['process_id' => $process_id, 'step_id' => $target['id'], 'status' => 2, 'prethread_id' => $thread['id']]);
                self::addThreadLog([
                        'thread_id' => $newThread_id,
                        'bill' => $bill,
                        'bill_id' => $bill_id,
                        'step_name' => $target['data']['name'],
                        'worker_id' => self::getSessionUid(),
                        'worker_name' => self::getUserById(self::getSessionUid())['username'],
                        'check_type' => 6,
                        'createtime' => (new \DateTime())->getTimestamp() + 1,
                        'updatetime' => (new \DateTime())->getTimestamp() + 1,
                    ]
                );
                self::endProcess($process_id);
                self::setBillStatus($bill, $bill_id, 2);
                self::sendMessage(['type' => 'end', 'bill' => $bill, 'bill_id' => $bill_id]);
            }
            elseif ($target['data']['type'] == 'condition') {
                $canrun_steps = self::getConditionCanRunStep($flow_id, $bill, $bill_id, $target);
                if (count($canrun_steps) == 0) {
                    return false;
                }
                foreach ($canrun_steps as $canrun_step) {
                    if ($canrun_step['data']['type'] == 'end') {
                        $newThread_id = self::addThread(['process_id' => $process_id, 'step_id' => $canrun_step['id'], 'status' => 2, 'prethread_id' => $thread['id']]);
                        self::addThreadLog([
                                'thread_id' => $newThread_id,
                                'bill' => $bill,
                                'bill_id' => $bill_id,
                                'step_name' => $canrun_step['data']['name'],
                                'worker_id' => self::getSessionUid(),
                                'worker_name' => self::getUserById(self::getSessionUid())['username'],
                                'check_type' => 6,
                                'createtime' => (new \DateTime())->getTimestamp() + 1,
                                'updatetime' => (new \DateTime())->getTimestamp() + 1,
                            ]
                        );
                        self::endProcess($process_id);
                        self::setBillStatus($bill, $bill_id, 2);
                        self::sendMessage(['type' => 'end', 'bill' => $bill, 'bill_id' => $bill_id]);
                    }
                    elseif ($canrun_step['data']['type'] == 'step') {
                        $scope = $canrun_step['data']['scope'];
                        $worker_ids = $canrun_step['data']['worker'];
                        $checkmode = $canrun_step['data']['checkmode'];
                        if ($canrun_step['data']['scope'] == 6) {
                            $scope = $param['scope'][$canrun_step['id']];
                            $worker_ids = $param['worker'][$canrun_step['id']];
                            $checkmode = $param['checkmode'][$canrun_step['id']];
                        }
                        $thread_id = self::addThread([
                                'process_id' => $process_id,
                                'step_id' => $canrun_step['id'],
                                'status' => 1,
                                'prethread_id' => $thread['id'],
                                'scope' => $scope,
                                'worker_ids' => $worker_ids,
                                'checkmode' => $checkmode,
                                'can_back' => $canrun_step['data']['back'],
                                'can_sign' => $canrun_step['data']['sign'],
                            ]
                        );
                        self::sendMessage(['type' => 'step', 'thread_id' => $thread_id]);
                    }
                }
                //添加条件流转日志
                self::addConditionCanRunLog($process_id, $target);
            }
            elseif ($target['data']['type'] == 'step') {
                if (!isset($target['data']['scope']) || !isset($target['data']['worker'])) {
                    return false;
                }
                $scope = $target['data']['scope'];
                $worker_ids = $target['data']['worker'];
                $checkmode = $target['data']['checkmode'];
                if ($target['data']['scope'] == 6) {
                    $scope = $param['scope'][$target['id']];
                    $worker_ids = $param['worker'][$target['id']];
                    $checkmode = $param['checkmode'][$target['id']];
                }
                $thread_id = self::addThread([
                        'process_id' => $process_id,
                        'step_id' => $target['id'],
                        'status' => 1,
                        'prethread_id' => $thread['id'],
                        'scope' => $scope,
                        'worker_ids' => $worker_ids,
                        'checkmode' => $checkmode,
                        'can_back' => $target['data']['back'],
                        'can_sign' => $target['data']['sign'],
                    ]
                );
                self::sendMessage(['type' => 'step', 'thread_id' => $thread_id]);
            }
        }
        return true;
    }


    public function getBillWorker($bill, $bill_id, $fileds)
    {
        $workers = [];
        $bill_row = self::getBillRow($bill, $bill_id);
        foreach ($fileds as $filed) {
            if (!isset($bill_row[$filed])) {
                return [];
            }
            $ids = explode(',', $bill_row[$filed]);
            foreach ($ids as $id) {
                $workers[] = $id;
            }
        }
        return $workers;
    }


    public function addThreadWorkerCheckedIds($bill, $bill_id, $thread)
    {
        if ($thread['scope'] == 1 || $thread['scope'] == 3) {
            $workers = explode(',', $thread['worker_ids']);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $agency_info = self::checkAgency($bill, $bill_id, $thread['scope'], array_diff($workers, $worker_checked_ids));
            if ($agency_info !== false) {
                foreach ($agency_info['principal_ids'] as $principal_id) {
                    $thread['worker_checked_ids'] .= ',' . $principal_id;
                }
            }
            else {
                $thread['worker_checked_ids'] .= ',' . self::getSessionUid();
            }
        }
        elseif ($thread['scope'] == 2) {
            $workers = explode(',', $thread['worker_ids']);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $agency_info = self::checkAgency($bill, $bill_id, $thread['scope'], array_diff($workers, $worker_checked_ids));
            if ($agency_info !== false) {
                foreach ($agency_info['principal_ids'] as $principal_id) {
                    $thread['worker_checked_ids'] .= ',' . $principal_id;
                }
            }
            else {
                $current_user_group_ids = self::getUserGroupIds(self::getSessionUid());
                foreach ($current_user_group_ids as $current_user_group_id) {
                    if (in_array($current_user_group_id, explode(',', $thread['worker_ids']))) {
                        $thread['worker_checked_ids'] .= ',' . $current_user_group_id;
                    }
                }
            }
        }
        elseif ($thread['scope'] == 4) {
            $bill_groups = self::getBillWorker($bill, $bill_id, explode(',', $thread['worker_ids']));
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $agency_info = self::checkAgency($bill, $bill_id, $thread['scope'], array_diff($bill_groups, $worker_checked_ids));
            if ($agency_info !== false) {
                foreach ($agency_info['principal_ids'] as $principal_id) {
                    $thread['worker_checked_ids'] .= ',' . $principal_id;
                }
            }
            else {
                $current_user_group_ids = self::getUserGroupIds(self::getSessionUid());
                foreach ($current_user_group_ids as $current_user_group_id) {
                    if (in_array($current_user_group_id, $bill_groups)) {
                        $thread['worker_checked_ids'] .= ',' . $current_user_group_id;
                    }
                }
            }
        }
        elseif ($thread['scope'] == 5) {
            $worker_ids = self::getWorkerIds($bill, $bill_id, $thread);
            $worker_checked_ids = explode(',', $thread['worker_checked_ids']);
            $agency_info = self::checkAgency($bill, $bill_id, $thread['scope'], array_diff($worker_ids, $worker_checked_ids));
            if ($agency_info !== false) {
                foreach ($agency_info['principal_ids'] as $principal_id) {
                    $thread['worker_checked_ids'] .= ',' . $principal_id;
                }
            }
            else {
                $user_group_ids = self::getUserGroupIds(self::getSessionUid());
                foreach ($user_group_ids as $user_group_id) {
                    $thread['worker_checked_ids'] .= ',' . $user_group_id;
                }
            }
        }
        $thread['worker_checked_ids'] = trim($thread['worker_checked_ids'], ',');
        return $thread;
    }


    public function getWorkerIds($bill, $bill_id, $thread)
    {
        $worker_ids = [];
        $scope = $thread['scope'];
        if ($scope == 1 || $scope == 2) {
            foreach (explode(',', $thread['worker_ids']) as $worker_id) {
                $worker_ids[] = $worker_id;
            }
        }
        elseif ($scope == 3 || $scope == 4) {
            $fileds = explode(',', $thread['worker_ids']);
            $bill_workers = self::getBillWorker($bill, $bill_id, $fileds);
            foreach ($bill_workers as $bill_worker) {
                $worker_ids[] = $bill_worker;
            }
        }
        elseif ($scope == 5) {
            $bill_row = self::getBillRow($bill, $bill_id);
            if ($bill_row) {
                $initiator_id = $bill_row['initiator_id'];
                $initiator_group_ids = self::getUserGroupIds($initiator_id);
                foreach ($initiator_group_ids as $initiator_group_id) {
                    $worker_ids[] = self::getGroup($initiator_group_id)[0]['pid'];
                }
            }
        }
        return $worker_ids;
    }


    public function setBillStatus($bill, $bill_id, $status)
    {
        return toolAdapter::callFn('setBillStatus', ['bill' => $bill, 'bill_id' => $bill_id, 'status' => $status]);
    }


    public function startFlow($param)
    {

        $flow_id = $param['flow_id'];
        $bill = $param['bill'];
        $bill_id = $param['bill_id'];


        if (!self::checkDynamicStepFormInput($param)) {
            return ['code' => 0, 'msg' => '审批人员不能为空'];
        }

        if (self::getProcess(['bill' => $bill, 'bill_id' => $bill_id])) {
            return ['code' => 0, 'msg' => '错误：发现该单据已存在进程，请联系管理员'];
        }

        $start_node = self::getNodeByType($flow_id, 'start')[0];
        if (!$start_node) {
            return ['code' => 0, 'msg' => '流程错误，请确保正确的流程设计'];
        }

        self::addProcess(['flow_id' => $flow_id, 'bill' => $bill, 'bill_id' => $bill_id, 'status' => 1]);
        $process_id = self::getProcess(['bill' => $bill, 'bill_id' => $bill_id])[0]['id'];

        $start_node_id = $start_node['id'];
        $start_thread_id = self::addThread([
                'process_id' => $process_id,
                'step_id' => $start_node_id,
                'scope' => 1,
                'worker_ids' => self::getSessionUid(),
                'status' => 1
            ]
        );
        self::setBillStatus($bill, $bill_id, 1);
        $param['thread_id'] = $start_thread_id;
        $param['start'] = true;
        return self::agreeFlow($param);
    }


    public function checkDynamicStepFormInput($param)
    {
        if (isset($param['scope']) && isset($param['worker']) && isset($param['checkmode'])) {
            foreach ($param['scope'] as $key => $param_scope) {
                if ($param_scope != '5') {
                    if ($param['worker'][$key] == '') {
                        return false;
                    }
                }
            }
        }
        return true;
    }


    public function getCanBackPreStep($flow_id, $process_id, $node)
    {
        $preSteps = [];
        $source_nodes = self::getSourceNodes($flow_id, $node);

        if (count($source_nodes) == 1) {
            if ($source_nodes[0]['data']['type'] == 'step' || $source_nodes[0]['data']['type'] == 'start') {
                $preSteps[] = $source_nodes[0];
            }
            elseif ($source_nodes[0]['data']['type'] == 'condition') {
                $condition_preSteps = self::getCanBackPreStep($flow_id, $process_id, $source_nodes[0]);
                foreach ($condition_preSteps as $condition_preStep) {
                    $preSteps[] = $condition_preStep;
                }
            }
        }
        elseif (count($source_nodes) > 1) {
            $commonSource = self::getCommonSourceNode($flow_id, $source_nodes);
            $all_must_check_steps = self::getAllMustCheckSteps($flow_id, $process_id, $commonSource, $node);
            foreach ($source_nodes as $source) {
                if (in_array($source, $all_must_check_steps)) {
                    $preSteps[] = $source;
                }
            }
        }
        return $preSteps;
    }

    public function dealCarbon($param, $current_log_id)
    {
        $scope = $param['carbon_scope'];
        $range = $param['carbon_range'];
        $carbon_receivers = explode(',', $param['carbon_receiver']);
        $log_ids = [];
        if ($range == 1) {//当前步骤日志
            $log_ids[] = $current_log_id;
        }
        elseif ($range == 2) {//全部已审批的步骤日志
            $log_rows = self::getThreadLog($param['bill'], $param['bill_id']);
            if ($log_rows) {
                foreach ($log_rows as $row) {
                    foreach ($row as $item) {
                        $log_ids[] = $item['id'];
                    }
                }
            }
        }
        if ($scope == 1) {//人员
            foreach ($carbon_receivers as $receiver) {
                self::addCarbon([
                    'bill' => $param['bill'],
                    'bill_id' => $param['bill_id'],
                    'sender_id' => self::getSessionUid(),
                    'receiver_id' => $receiver,
                    'sendtime' => date("Y-m-d H:i:s"),
                    'thread_log_ids' => implode(',', $log_ids),
                    'is_read' => 0,
                ]);
            }
        }
        elseif ($scope == 2) {//分组
            $user_ids = [];
            foreach ($carbon_receivers as $receiver) {
                $ids = self::getGroupUserIds($receiver);
                if ($ids) {
                    foreach ($ids as $id) {
                        if (!in_array($id, $user_ids)) {
                            $user_ids[] = $id;
                        }
                    }
                }
            }
            foreach ($user_ids as $user_id) {
                self::addCarbon([
                    'bill' => $param['bill'],
                    'bill_id' => $param['bill_id'],
                    'sender_id' => self::getSessionUid(),
                    'receiver_id' => $user_id,
                    'sendtime' => date("Y-m-d H:i:s"),
                    'thread_log_ids' => implode(',', $log_ids),
                    'is_read' => 0,
                ]);
            }
        }
    }

    public function agreeFlow($param)
    {
        $thread_id = $param['thread_id'];
        $thread = self::getThread(['id' => $thread_id])[0];
        $process = self::getProcess(['id' => $thread['process_id']])[0];
        $flow_id = $process['flow_id'];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];
        $checkcontent = isset($param['checkcontent']) ? $param['checkcontent'] : '';
        $file = isset($param['file']) ? $param['file'] : '';
        $file_name = isset($param['file_name']) ? $param['file_name'] : '文件';


        if (!self::checkDynamicStepFormInput($param)) {
            return ['code' => 0, 'msg' => '审批人员不能为空'];
        }

        if (!self::getProcess(['bill' => $bill, 'bill_id' => $bill_id])) {
            return ['code' => 0, 'msg' => '错误：未发现该单据的进程，请联系管理员'];
        }

        if (!isset($param['start'])){
            $auth_info = self::checkThreadAuth(self::getThread(['id' => $thread_id])[0]);
            if ($auth_info === false) {
                return ['code' => 0, 'msg' => '您没有审批权限'];
            }
        }
        else{
            $auth_info['agency'] = false;
        }

        $current_thread = self::getThread(['id' => $thread_id])[0];
        if (!$current_thread) {
            return ['code' => 0, 'msg' => '错误：未找到该线程，请联系管理员'];
        }

        $step = self::getNodeById($flow_id, $current_thread['step_id']);
        $step_name = $step['data']['name'];
        $step_type = $step['data']['type'];
        $log_id = self::addThreadLog([
                'thread_id' => $thread_id,
                'bill' => $bill,
                'bill_id' => $bill_id,
                'step_name' => $step_name,
                'worker_id' => self::getSessionUid(),
                'worker_name' => self::getUserById(self::getSessionUid())['username'],
                'check_content' => $checkcontent,
                'file' => $file,
                'file_name' => $file_name,
                'check_type' => $step_type == 'start' ? 5 : 1,
                'is_agency' => $auth_info['agency'] ? 1 : 0,
                'principal' => $auth_info['agency'] === true ? $auth_info['agency_info']['principal_name'] : '',
                'agent' => $auth_info['agency'] === true ? $auth_info['agency_info']['agent_name'] : '',
                'createtime' => (new \DateTime())->getTimestamp(),
                'updatetime' => (new \DateTime())->getTimestamp(),
            ]
        );

        if (isset($param['carbon_receiver']) && $param['carbon_receiver'] !== '') {
            $param['bill'] = $bill;
            $param['bill_id'] = $bill_id;
            self::dealCarbon($param, $log_id);
        }

        $current_thread = self::addThreadWorkerCheckedIds($bill, $bill_id, $current_thread);

        if ($current_thread['checkmode'] == 1) {
            $worker_ids = self::getWorkerIds($bill, $bill_id, $current_thread);
            $worker_checked_ids = explode(',', $current_thread['worker_checked_ids']);
            $diff = array_diff($worker_ids, $worker_checked_ids);
            if (count($diff) == 0) {
                self::endThread($current_thread);
                if (self::checkWhetherCanFLow($flow_id, $current_thread['process_id'], self::getNodeById($flow_id, $current_thread['step_id']))) {
                    if (!self::startTargetsThread($param)) {
                        self::setProcess(['id' => $current_thread['process_id'], 'status' => 3]);
                        return ['code' => 0, 'msg' => '流程执行错误，请检查流程设计是否完善'];
                    }
                }
            }
            else {
                self::setThread($current_thread);
            }
        }
        elseif ($current_thread['checkmode'] == 2) {
            self::endThread($current_thread);
            if (self::checkWhetherCanFLow($flow_id, $current_thread['process_id'], self::getNodeById($flow_id, $current_thread['step_id']))) {
                if (!self::startTargetsThread($param)) {
                    self::setProcess(['id' => $current_thread['process_id'], 'status' => 3]);
                    return ['code' => 0, 'msg' => '流程执行错误，请检查流程设计是否完善'];
                }
            }
        }
        return ['code' => 1, 'msg' => '操作成功'];

    }


    public function signFlow($param)
    {
        $thread_id = $param['thread_id'];
        $threads = self::getThread(['id' => $thread_id]);

        if (!$threads) {
            return ['code' => 0, 'msg' => '错误：未找到该线程，请联系管理员'];
        }
        $thread = $threads[0];
        $auth_info = self::checkThreadAuth($thread);
        if ($auth_info === false) {
            return ['code' => 0, 'msg' => '您没有审批权限'];
        }

        if ($thread['can_sign'] == 0) {
            return ['code' => 0, 'msg' => '错误：该步骤没有会签权限'];
        }
        if (!isset($param['sign_scope']) || !isset($param['sign_worker']) || !isset($param['sign_checkmode'])) {
            return ['code' => 0, 'msg' => '错误：表单参数不完整'];
        }

        if ($param['sign_worker'] == '') {
            return ['code' => 0, 'msg' => '错误：审批人员不能为空'];
        }

        $process = self::getProcess(['id' => $thread['process_id']])[0];
        $flow_id = $process['flow_id'];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];
        $checkcontent = isset($param['checkcontent']) ? $param['checkcontent'] : '';
        $file = isset($param['file']) ? $param['file'] : '';
        $file_name = isset($param['file_name']) ? $param['file_name'] : '文件';


        $step = self::getNodeById($flow_id, $thread['step_id']);
        $step_name = $step['data']['name'];
        $log_id = self::addThreadLog([
                'thread_id' => $thread_id,
                'bill' => $bill,
                'bill_id' => $bill_id,
                'step_name' => $step_name,
                'worker_id' => self::getSessionUid(),
                'worker_name' => self::getUserById(self::getSessionUid())['username'],
                'check_content' => $checkcontent,
                'file' => $file,
                'file_name' => $file_name,
                'check_type' => 3,
                'sign_worker' => self::getWorkerNameStr(explode(',', $param['sign_worker']), $param['sign_scope']),
                'sign_checkmode' => $param['sign_checkmode'],
                'is_agency' => $auth_info['agency'] ? 1 : 0,
                'principal' => $auth_info['agency'] === true ? $auth_info['agency_info']['principal_name'] : '',
                'agent' => $auth_info['agency'] === true ? $auth_info['agency_info']['agent_name'] : '',
                'createtime' => (new \DateTime())->getTimestamp(),
                'updatetime' => (new \DateTime())->getTimestamp(),
            ]
        );

        if (isset($param['carbon_receiver']) && $param['carbon_receiver'] !== '') {
            $param['bill'] = $bill;
            $param['bill_id'] = $bill_id;
            self::dealCarbon($param, $log_id);
        }

        $thread = self::addThreadWorkerCheckedIds($bill, $bill_id, $thread);

        self::endThread($thread);

        $thread['status'] = 1;
        $thread['is_back'] = 0;
        $thread['is_sign'] = 1;
        $thread['scope'] = $param['sign_scope'];
        $thread['worker_ids'] = $param['sign_worker'];
        $thread['checkmode'] = $param['sign_checkmode'];
        $thread['worker_checked_ids'] = '';
        $thread['can_back'] = $param['sign_canback'];
        $thread['can_sign'] = $param['sign_cansign'];
        $thread['id'] = '';
        $thread_id = self::addThread($thread);
        if ($thread_id) {
            self::sendMessage(['type' => 'step', 'thread_id' => $thread_id]);
            return ['code' => 1, 'msg' => '操作成功'];
        }
    }


    public function backFlow($param)
    {
        $thread_id = $param['thread_id'];
        $threads = self::getThread(['id' => $thread_id]);

        if (!$threads) {
            return ['code' => 0, 'msg' => '错误：未找到该线程，请联系管理员'];
        }
        $thread = $threads[0];
        $auth_info = self::checkThreadAuth($thread);
        if ($auth_info === false) {
            return ['code' => 0, 'msg' => '您没有审批权限'];
        }

        if ($thread['can_back'] == 0) {
            return ['code' => 0, 'msg' => '错误：该步骤没有驳回权限'];
        }
        $back_steps_id = $param['back_steps'];
        if (count($back_steps_id) == 1 && $back_steps_id[0] == '') {
            return ['code' => 0, 'msg' => '错误：回退步骤不能为空'];
        }

        $process = self::getProcess(['id' => $thread['process_id']])[0];
        $flow_id = $process['flow_id'];
        $bill = $process['bill'];
        $bill_id = $process['bill_id'];
        $checkcontent = isset($param['checkcontent']) ? $param['checkcontent'] : '';
        $file = isset($param['file']) ? $param['file'] : '';
        $file_name = isset($param['file_name']) ? $param['file_name'] : '文件';

        $back_steps = [];
        $back_steps_name_arr = [];
        foreach ($back_steps_id as $back_step_id) {
            $back_step = self::getNodeById($flow_id, $back_step_id);
            $back_steps[] = $back_step;
            $back_steps_name_arr[] = $back_step['data']['name'];
        }
        $back_steps_name_str = implode(',', $back_steps_name_arr);

        $step = self::getNodeById($flow_id, $thread['step_id']);
        $step_name = $step['data']['name'];
        $log_id = self::addThreadLog([
                'thread_id' => $thread_id,
                'bill' => $bill,
                'bill_id' => $bill_id,
                'step_name' => $step_name,
                'worker_id' => self::getSessionUid(),
                'worker_name' => self::getUserById(self::getSessionUid())['username'],
                'check_content' => $checkcontent,
                'file' => $file,
                'file_name' => $file_name,
                'check_type' => 2,
                'back_steps' => $back_steps_name_str,
                'is_agency' => $auth_info['agency'] ? 1 : 0,
                'principal' => $auth_info['agency'] === true ? $auth_info['agency_info']['principal_name'] : '',
                'agent' => $auth_info['agency'] === true ? $auth_info['agency_info']['agent_name'] : '',
                'createtime' => (new \DateTime())->getTimestamp(),
                'updatetime' => (new \DateTime())->getTimestamp(),
            ]
        );

        if (isset($param['carbon_receiver']) && $param['carbon_receiver'] !== '') {
            $param['bill'] = $bill;
            $param['bill_id'] = $bill_id;
            self::dealCarbon($param, $log_id);
        }

        $thread = self::addThreadWorkerCheckedIds($bill, $bill_id, $thread);

        self::endThread($thread);


        foreach ($back_steps_id as $back_step_id) {

            $back_step = self::getNodeById($flow_id, $back_step_id);

            if ($back_step['data']['type'] == 'start') {
                $all_running_threads = self::getThread(['process_id' => $process['id'], 'status' => 1]);
                if ($all_running_threads) {
                    foreach ($all_running_threads as $running_thread) {
                        $running_thread['status'] = 3;
                        self::setThread($running_thread);
                    }
                }
                if (self::delProcess($process)) {
                    self::setBillStatus($bill, $bill_id, 0);
                    self::sendMessage(['type' => 'start', 'bill' => $bill, 'bill_id' => $bill_id]);
                    return ['code' => 1, 'msg' => '操作成功'];
                }
                else {
                    return ['code' => 0, 'msg' => '驳回至重新发起错误，未找到相关进程，请联系管理员'];
                }
            }
            $targets = self::getTargetNodes($flow_id, $back_step);

            $end_step = null;
            if (count($targets) == 1) {
                $end_step = self::getNodeByType($flow_id, 'end')[0];
            }
            elseif (count($targets) > 1) {
                $end_step = self::getCommonDestinationNode($flow_id, $targets);
            }
            $all_must_check_steps = self::getAllMustCheckSteps($flow_id, $process['id'], $back_step, $end_step);
            foreach ($all_must_check_steps as $must_check_step) {
                $step_threads = self::getThread(['process_id' => $process['id'], 'step_id' => $must_check_step['id'], 'status' => 1]);
                if ($step_threads) {
                    $step_thread = $step_threads[0];
                    $step_thread['status'] = 3;
                    if (!self::setThread($step_thread)) {
                        return ['code' => 0, 'msg' => '错误：分支线程' . $step_thread['id'] . '未能设为终止'];
                    }
                }
            }

            $pre_thread = self::getThread(['process_id' => $process['id'], 'step_id' => $back_step_id, 'status' => 2, 'is_back' => 0, 'is_sign' => 0])[0];

            $pre_thread['worker_checked_ids'] = '';
            $pre_thread['status'] = 1;
            $pre_thread['is_back'] = 1;
            if (!self::setThread($pre_thread)) {
                return ['code' => 0, 'msg' => '错误：驳回线程错误，线程' . $pre_thread['id'] . '状态未能更新'];
            }
            self::sendMessage(['type' => 'step', 'thread_id' => $pre_thread['id']]);

        }
        return ['code' => 1, 'msg' => '操作成功'];
    }


    public function getThreadLog($bill, $bill_id)
    {
        $logs = toolAdapter::callFn('getThreadLog', ['bill' => $bill, 'bill_id' => $bill_id]);
        if (!$logs) {
            return false;
        }
        $date_temp = [];
        foreach ($logs as $log) {
            $log_date = date("Y年m月d日", $log['createtime']);
            $log['createtime'] = $log['updatetime'] = date("Y/m/d H:i:s", $log['createtime']);
            $log['shorttime'] = str_replace($log_date . ' ', '', $log['createtime']);
            $log['longtime'] = $log['createtime'];
            $date_temp[$log_date][] = $log;
        }
        return $date_temp;
    }

    public function getLogByIds($bill, $bill_id, $log_ids)
    {
        $logs = toolAdapter::callFn('getThreadLogByIds', ['condition' => ['bill' => $bill, 'bill_id' => $bill_id], 'ids' => $log_ids]);
        if (!$logs) {
            return false;
        }
        $date_temp = [];
        foreach ($logs as $log) {
            $log_date = date("Y年m月d日", $log['createtime']);
            $log['createtime'] = $log['updatetime'] = date("Y年m月d日 H:i:s", $log['createtime']);
            $log['shorttime'] = str_replace($log_date . ' ', '', $log['createtime']);
            $log['longtime'] = $log['createtime'];
            $date_temp[$log_date][] = $log;
        }
        return $date_temp;
    }

    public function delProcess($param)
    {
        return processAdapter::callFn('delProcess', $param);
    }

    public function test()
    {
        $thread = self::getThread(['id' => 291])[0];
        self::sendMessage(['type' => 'step', 'thread_id' => $thread['id']]);
    }

}